package com.dai.nettyserve.netty.serve;

import com.dai.nettyserve.netty.codec.IODecoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetSocketAddress;

/**
 * @Author: 代
 * @Description:netty服务器配置
 * @Date: Created in 10:20 2017/3/25
 */
@Component
@Order(value = 2)//这里表示启动顺序
//实现ApplicationContextAware以获得ApplicationContext中的所有bean
public class NettyServer implements ApplicationRunner, ApplicationListener<ContextClosedEvent>, ApplicationContextAware {
    @Resource
    private DataServerInHandler dataServerInHandler;
    private static final Logger LOGGER = LoggerFactory.getLogger(NettyServer.class);
    private ApplicationContext applicationContext;
    private Channel serverChannel;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    @Async//注意这里，组件启动时会执行run，这个注解是让线程异步执行，这样不影响主线程
    public void run(ApplicationArguments args) throws Exception {

        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        MetricsHandler metricsHandler  = new MetricsHandler();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup);
            serverBootstrap.channel(NioServerSocketChannel.class);
            //serverBootstrap.localAddress(new InetSocketAddress("0.0.0.0", 9962));
            serverBootstrap.localAddress(new InetSocketAddress("0.0.0.0", 8090));
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    //设置读写空闲事件，触发后移除该设备通道链路
                    pipeline.addLast(new IdleStateHandler(60, 60, 60))
                            //真实数据最大字节数为Integer.MAX_VALUE，解码时自动去掉前面四个字节
                            //io.netty.handler.codec.DecoderException: java.lang.IndexOutOfBoundsException: readerIndex(900) + length(176) exceeds writerIndex(1024): UnpooledUnsafeDirectByteBuf(ridx: 900, widx: 1024, cap: 1024)
                            //.addLast("logging", new LoggingHandler(LogLevel.INFO))
                            //日志先注释掉    因为打印太多控制台卡死
                            .addLast("logging", new LoggingHandler(LogLevel.INFO))
                            .addLast(new IODecoder())
                            .addLast(new StringEncoder())//添加字符串编码器
                            .addLast(new ByteArrayEncoder())//添加字节数组的编码器
                            .addLast(dataServerInHandler)
                            .addLast("metricHandler", metricsHandler);    //检测客户端连接数
                }
            });
            Channel channel = serverBootstrap.bind().sync().channel();
            this.serverChannel = channel;

            LOGGER.info("Netty 服务启动，ip={},port={}", "0.0.0.0", 8090);
            channel.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    @PreDestroy
    public void stop() {
        if (serverChannel != null) {
            LOGGER.info("----------Netty Server close-----------");
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        if (this.serverChannel != null) {
            this.serverChannel.close();
        }
        LOGGER.info("websocket 服务停止");
    }
}
